package worker

import (
	"GoCrontab/crontab/common"
	"context"
	"fmt"
	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/mvcc/mvccpb"
	"time"
)

//与master中监听浏览器的curd请求不同，worker主要是监听etcd的变化
var (
	GlobalJobManager *JobManager
)

//master的任务管理器
type JobManager struct {
	client *clientv3.Client
	//定义client v3子集
	kv      clientv3.KV
	lease   clientv3.Lease
	watcher clientv3.Watcher
}

func (jobManager *JobManager) WatchJobs() (err error) {
	var (
		responseGet *clientv3.GetResponse
		kvPair      *mvccpb.KeyValue
		//定义一个job指针，接受kvPair的value的 Unmarshal
		job                   *common.Job
		watchStartRevision    int64
		watchChan             clientv3.WatchChan
		watchResponseFromChan clientv3.WatchResponse
		//推送给schduler的事件任务
		jobEvent *common.JobEvent
	)
	//1. 获取到save job目录下的所有任务，获取当前集群的revision
	if responseGet, err = jobManager.kv.Get(context.TODO(), common.JOB_SAVE_DIR, clientv3.WithPrefix(), clientv3.WithPrevKV()); err != nil {
		return nil
	}
	//获取全量任务的时候也需要推给Scheduler
	for _, kvPair = range responseGet.Kvs {
		if job, err = common.UnpackJob(kvPair.Value); err == nil {
			jobEvent = common.BuildJobEvent(common.EVENT_JOB_SAVE, job)
			fmt.Println(string(kvPair.Key), "来了,我要把你存起来，", string(kvPair.Value))
			GlobalScheduler.PushJobEvent(jobEvent)
		}
	}
	//2.启动一个监听协程， 最后的revision+1，监听event时间{put &&delete}
	go func() {
		watchStartRevision = responseGet.Header.Revision + 1
		watchChan = jobManager.watcher.Watch(context.TODO(), common.JOB_SAVE_DIR, clientv3.WithPrefix(), clientv3.WithRev(watchStartRevision))

		//channel中有kv变化的时候，会将response投递到channel中 ，遍历即可
		for watchResponseFromChan = range watchChan {
			//events 是 mvccpb.Event数组，事件类型为put 和delete，包括put为新的kv，以及覆盖掉的preKV。这里可以再回头看看createRevision,ModRevision相关。如果是删除操作，此时create_revision归0，后续再创建，create_version 为mod_creation+1。注意这个events可能有其他key的变化，取决你watch的参数，events是个数组指针
			for _, ev := range watchResponseFromChan.Events {
				//解析方法1
				//	fmt.Printf("===开始监听,事件类型为:%s。 key为%q : value为%q,创建revision为%d,修改revision为%d\n", ev.Type, ev.Kv.Key, ev.Kv.Value, ev.Kv.CreateRevision, ev.Kv.ModRevision)
				//	解析event方法2
				switch ev.Type {
				case mvccpb.PUT:
					fmt.Printf("=====%s事件【cron save】,key为%q,value为%q,创建Create revision为%d,修改Mod revision为%d\n", ev.Type, ev.Kv.Key, ev.Kv.Value, ev.Kv.CreateRevision, ev.Kv.ModRevision)
					if job, err = common.UnpackJob(ev.Kv.Value); err != nil {
						continue
					}
					//unmarshal 成json，构建一个更新event，然后推送给schedule更新|新建事件
					jobEvent = common.BuildJobEvent(common.EVENT_JOB_SAVE, job)
				case mvccpb.DELETE:
					fmt.Printf("=====%s事件【cron delete】,key为%q,value为%q,[注意]创建Create revision为%d,修改 Mod revision为%d\n", ev.Type, ev.Kv.Key, ev.Kv.Value, ev.Kv.CreateRevision, ev.Kv.ModRevision)
					//删除通过ev.kv.key拿到的是/cron/jobs/xxName ，这里只需要拿到xxName，然后再投递给scheduler
					//和下面写法一效果？
					//job=&common.Job{Name:common.ExtractJobName(string(ev.Kv.Key))}
					job.Name = common.ExtractJobName(string(ev.Kv.Key))
					//unmarshal 成json，构建一个删除event，然后推送给schedule删除事件
					jobEvent = common.BuildJobEvent(common.EVENT_JOB_DELETE, job)
				}
				//@todo 以上两个todo的结合
				GlobalScheduler.PushJobEvent(jobEvent)
			}
		}
	}()
	return
}

// 创建任务执行锁
func (jobManager *JobManager) CreateJobLock(jobName string) (jobLock *JobLock) {
	jobLock = InitJobLock(jobName, jobManager.kv, jobManager.lease)
	return
}

func (jobManager *JobManager) watchKiller() {

}

//etcd --listen-client-urls 'http://0.0.0.0:2379' --advertise-client-urls 'http://0.0.0.0:2379'
func InitJobManager() (err error) {
	var (
		config  clientv3.Config
		client  *clientv3.Client
		kv      clientv3.KV
		lease   clientv3.Lease
		watcher clientv3.Watcher
	)
	config = clientv3.Config{
		Endpoints: GlobalConfig.EtcdEndPoints,
		Username:  GlobalConfig.EtcdUserName,
		Password:  GlobalConfig.EtcdPassword,
		//Millisecond 毫秒,1毫秒等于千分之一秒
		// MicroSecond 微秒,1微秒等于百万分之一秒
		DialTimeout: time.Duration(GlobalConfig.EtcdDialTimeout) * time.Millisecond,
	}

	if client, err = clientv3.New(config); err != nil {
		return
	}
	//得到KV和Lease的api子集
	kv = clientv3.NewKV(client)
	lease = clientv3.NewLease(client)
	watcher = clientv3.NewWatcher(client)

	fmt.Println("ectd success")

	GlobalJobManager = &JobManager{
		client:  client,
		kv:      kv,
		lease:   lease,
		watcher: watcher,
	}

	GlobalJobManager.WatchJobs()
	GlobalJobManager.watchKiller()

	return

}
